package com.google.code.mamule.price;

import com.google.code.mamule.mail.Mail;
import com.google.code.mamule.mail.Mailable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.EnumMap;
import java.util.Map;

import javax.annotation.Nullable;

/**
 * Created with IntelliJ IDEA. User: borck_000 ; Date: 1/7/14 ; Time: 8:27 PM
 */
@Service
@Transactional(propagation = Propagation.MANDATORY)
public class ZoneBasedPricing implements PricingStrategy {

  private final static Logger log = LoggerFactory.getLogger(ZoneBasedPricing.class);

  PriceRepository prices;

  @Autowired
  public ZoneBasedPricing(final PriceRepository prices) {
    this.prices = prices;
  }

  private Map<PriceRegion, BigDecimal> pricesByRegion() {
    Map<PriceRegion, BigDecimal> out = new EnumMap<>(PriceRegion.class);
    for (Price each : prices.findLatestPrice()) {
      if (isValidEntry(each)) {
        out.put(each.getPriceRegion(), each.getPrice());
      }
    }
    return out;
  }

  // TODO : workaround for corrupt database
  private boolean isValidEntry(final Price each) {
    if (each.getPrice() == null || each.getPriceRegion() == null) {
      log.warn("ignoring illegal price {}", each);
      return false;
    }
    return true;
  }

  private BigDecimal priceForPostcode(String postcode, Map<PriceRegion, BigDecimal> priceMap) {
    BigDecimal price = BigDecimal.ZERO;
    switch (postcode.length()) {
      case 3:
        price = priceMap.get(PriceRegion.EU);
        break;
      case 4:
        price = priceMap.get(PriceRegion.NONEU);
        break;
      default:
        price = priceMap.get(PriceRegion.UNDEF);
    }
    // ignore db corruption
    if (price == null) {
      log.warn("no valid price found for postcode {} using 1 instead", postcode);
      price = BigDecimal.ONE;
    }
    return price;
  }

  @Nullable
  @Override
  public BigDecimal apply(@Nullable final Mail input) {
    BigDecimal total = BigDecimal.ZERO;
    Map<PriceRegion, BigDecimal> priceMap = pricesByRegion();
    for (Mailable each : input.getReceiver()) {
      total = total.add(priceForPostcode(each.getAddress().getPostcode(), priceMap));
    }
    return total;
  }
}
